home *** CD-ROM | disk | FTP | other *** search
- /*
- ** Apple Macintosh Developer Technical Support
- **
- ** Program: CShell
- ** File: AppleEvents.c
- ** Written by: Keith Rollin
- **
- ** Copyright © 1990-1991 Apple Computer, Inc.
- ** All rights reserved.
- **
- ** This code is completely based on the great work done by Keith Rollin.
- ** All I did was to add more comments than _anybody_ would want, make some
- ** things a little more general, and to set the code up so the the custom
- ** events are handled in a separate file.
- */
-
-
-
- /*****************************************************************************/
-
-
-
- #include "CShell.h" /* Get the CShell includes/typedefs, etc. */
- #include "CShellCommon.h" /* Get the stuff in common with rez. */
- #include "CShell.protos" /* Get the prototypes for CShell. */
-
- #ifndef __GESTALTEQU__
- #include <GestaltEqu.h>
- #endif
-
- #ifndef __UTILITIES__
- #include "Utilities.h"
- #endif
-
-
-
- /*****************************************************************************/
-
-
-
- #ifndef THINK_C
- #define rErrorAlert 129
- #endif
- #define kTimeOutInTicks (60 * 30) /* 30 second timeout. */
-
-
-
- /*****************************************************************************/
-
-
-
- struct triplets{
- AEEventClass theEventClass;
- AEEventID theEventID;
- ProcPtr theHandler;
- };
- typedef struct triplets triplets;
- static triplets keywordsToInstall[] = {
- { kCoreEventClass, kAEOpenApplication, (ProcPtr) DoAEOpenApplication },
- { kCoreEventClass, kAEOpenDocuments, (ProcPtr) DoAEOpenDocuments },
- { kCoreEventClass, kAEPrintDocuments, (ProcPtr) DoAEPrintDocuments },
- { kCoreEventClass, kAEQuitApplication, (ProcPtr) DoAEQuitApplication }
- /* The above are the four required AppleEvents. */
- };
-
- Boolean gHasAppleEvents = false;
- Boolean gHasPPCToolbox = false;
-
-
-
- /*****************************************************************************/
-
-
-
- extern Boolean gQuitApplication;
- extern Cursor *gCurrentCursor;
- extern short gPrintPage;
-
-
-
- /*****************************************************************************/
- /*****************************************************************************/
-
-
-
- /* DoHighLevelEvent
- **
- ** Simply calls AEProcessAppleEvent and reports any errors.
- ** AEProcessAppleEvent looks in its table of registered events and sees if
- ** the current event is registered. If so, it calls the routine associated
- ** with that event. In our case, we set that to DispatchAppleEvent.
- ** DispatchAppleEvent handles it in all cases. DispatchAppleEvent uses the
- ** refCon to determine which AppleEvent it is doing, since it gets them all.
- */
-
- #pragma segment AppleEvents
- void DoHighLevelEvent(EventRecord *event)
- {
- AEProcessAppleEvent(event);
- }
-
-
-
- /*****************************************************************************/
-
-
-
- /* GetTargetInfo
- **
- */
-
- #pragma segment AppleEvents
- OSErr GetTargetInfo(AEAddressDesc targetDesc, StringPtr zone,
- StringPtr machine, StringPtr application)
- {
- ProcessSerialNumber targetPSN;
- PortInfoRec portInfo;
- TargetID theTargetID;
- OSErr err;
-
- zone[0] = 0;
- machine[0] = 0;
- application[0] = 0;
- err = noErr;
-
- if (targetDesc.descriptorType == typeProcessSerialNumber) {
- targetPSN = **(ProcessSerialNumber **)(targetDesc.dataHandle);
- err = GetPortNameFromProcessSerialNumber(&portInfo.name, &targetPSN);
- if (!err) pstrcpy((char *) application, (char *) &portInfo.name.name);
- return(err);
- }
-
- if (targetDesc.descriptorType == typeTargetID) {
- theTargetID = **(TargetID **)(targetDesc.dataHandle);
- switch (theTargetID.location.locationKindSelector) {
- case ppcNoLocation:
- break;
- case ppcNBPLocation:
- pstrcpy((char *) &zone, (char *) &theTargetID.location.u.nbpEntity.zoneStr);
- pstrcpy((char *) &machine, (char *) &theTargetID.location.u.nbpEntity.objStr);
- break;
- case ppcNBPTypeLocation:
- break;
- }
- pstrcpy((char *) &application, (char *) &theTargetID.name.name);
- return(noErr);
- }
-
- return(errAEWrongDataType);
- }
-
-
-
- /*****************************************************************************/
-
-
-
- /* InitAppleEvents
- **
- ** Intialize our AppleEvent dispatcher table. For every triplet of entries in
- ** keywordsToInstall, we make a call to AEInstallEventHandler().
- */
-
- #pragma segment AppleEvents
- void InitAppleEvents(void)
- {
- OSErr err;
- long result;
- short i;
-
- gHasPPCToolbox = (Gestalt(gestaltPPCToolboxAttr, &result) ? false : result != 0);
- gHasAppleEvents = (Gestalt(gestaltAppleEventsAttr, &result) ? false : result != 0);
-
- if (gHasAppleEvents) {
- for (i = 0; i < (sizeof(keywordsToInstall) / sizeof(triplets)); ++i) {
- err = AEInstallEventHandler(
- keywordsToInstall[i].theEventClass, /* What class to install. */
- keywordsToInstall[i].theEventID, /* Keywords to install. */
- keywordsToInstall[i].theHandler, /* The AppleEvent handler. */
- 0L, /* Unused refcon. */
- false /* Only for our app. */
- );
-
- if (err) {
- Alert(rErrorAlert, nil);
- return;
- }
- }
- }
- }
-
-
-
- /*****************************************************************************/
-
-
-
- /* MakeTarget
- **
- ** Creates a TargetID.
- **
- ** If sendDirect is TRUE, the target is specified by setting a
- ** ProcessSerialNumber to kCurrentProcess. This has the advantage of sending
- ** the message directly to ourselves, bypassing ePPC and gaining about a 10-15x
- ** speed improvement. If sendDirect is FALSE, we see if we have the
- ** PPCToolBox. If not, then we are forced to do a direct send. If we do have
- ** the PPCToolbox, then we call PPCBrowser. We then look at the reply, and
- ** factor in the mode we are going to use in AESend. If that mode is
- ** kAEWaitReply and the user selected us as the target, we have to turn that
- ** into a direct send. This is because the AppleEvent Manager will otherwise
- ** post the event as a high-level event. However, we are busy waiting for a
- ** reply, not looking for events, so we'll hang. We avoid this by forcing a
- ** direct send.
- */
-
- #pragma segment AppleEvents
- OSErr MakeTarget(AEAddressDesc *target, Boolean sendDirect, short replyMode,
- Str255 prompt, Str255 applListLabel,PPCFilterProcPtr portFilter)
- {
- OSErr err;
- ProcessSerialNumber targetPSN;
- ProcessSerialNumber myPSN;
- TargetID theTargetID;
- Boolean sendingToSelf;
-
- static LocationNameRec location;
- static PortInfoRec portInfo;
- static Boolean defaultOK = false;
-
- err = noErr; /* Make sure we do the code for the second main if. */
-
- target->dataHandle = nil;
- /* Assume we will fail and nil this descriptor out. */
-
- if (!sendDirect) {
- if (!gHasPPCToolbox)
- sendDirect = true; /* No tools to send with, so send direct. */
-
- else { /* We are not sending to self. */
- /* sendDirect is false. */
- err = PPCBrowser(
- prompt, /* Browse dialog box prompt. */
- applListLabel, /* The 'programs' list title. */
- defaultOK, /* Initially false. */
- &location, /* Correct if defaultOK is true. */
- &portInfo, /* Correct if defaultOK is true. */
- portFilter, /* No port filtering. */
- nil /* List ports of type 'PPCToolBox'. */
- );
-
- if (!err) { /* If user didn't cancel... */
- defaultOK = true; /* Default to the same port next time. */
- if (replyMode == kAEWaitReply) {
- /* Sender wants a reply and will be waiting... */
-
- sendingToSelf = false;
- /* Assume that we aren't sending to ourselves. */
-
- if (!location.locationKindSelector) {
- /* Hey, we are sending to ourselves! */
-
- err = GetProcessSerialNumberFromPortName(
- &portInfo.name, &targetPSN);
- if (!err) {
- GetCurrentProcess(&myPSN);
- err = SameProcess(&targetPSN, &myPSN, &sendingToSelf);
- }
- }
-
- if (sendingToSelf) sendDirect = true;
-
- }
- }
- }
- }
-
- if (!err) {
- if (sendDirect) {
- /* Finally, we get to the point... */
-
- targetPSN.highLongOfPSN = 0;
- targetPSN.lowLongOfPSN = kCurrentProcess;
- /* Process serial # is equal to kCurrentProcess. This
- ** bypasses ePPC and speeds up things considerably. */
-
- err = AECreateDesc(
- typeProcessSerialNumber, /* Standard PSN descriptor type. */
- (Ptr)&targetPSN, /* "No ePPC" process serial #. */
- sizeof(targetPSN), /* Size of data (2 longs). */
- target /* Wherefore art thou desc. */
- );
- }
- else {
- theTargetID.location = location;
- theTargetID.name = portInfo.name;
- /* The fields sessionID does not need to be filled in now.
- ** The sessionID is returned when you actually connect to
- ** a port. You can then use the sessionID from that point
- ** on to improve speed.
- **
- ** You also don't need to fill in the recvrName field at this
- ** point. This is filled in, again, when the session is
- ** actually established.
- **
- ** The amount of data for a non-us target is bigger.
- ** We need the whole dealie for our target, since
- ** it is out on the net somewhere. */
-
- err = AECreateDesc(
- typeTargetID, /* Standard target descriptor type. */
- (Ptr)&theTargetID, /* The data for the descriptor. */
- sizeof(theTargetID), /* Size of the data. */
- target /* Wherefore art thou desc. */
- );
- }
- }
- return (err);
- }
-
-
-
- /*****************************************************************************/
-
-
-
- /* MissedAnyParameters
- **
- ** Used to check for any unread required parameters. Returns true if we
- ** missed at least one.
- */
-
- #pragma segment AppleEvents
- Boolean MissedAnyParameters(AppleEvent *message)
- {
- OSErr err;
- DescType ignoredActualType;
- AEKeyword missedKeyword;
- Size ignoredActualSize;
- EventRecord event;
-
- err = AEGetAttributePtr( /* SEE IF PARAMETERS ARE ALL USED UP. */
- message, /* AppleEvent to check. */
- keyMissedKeywordAttr, /* Look for unread parameters. */
- typeKeyword, /* So we can see what type we missed, if any. */
- &ignoredActualType, /* What is would have been if not coerced. */
- (Ptr)&missedKeyword, /* Data area. (Keyword not handled.) */
- sizeof(missedKeyword), /* Size of data area. */
- &ignoredActualSize /* Actual data size. */
- );
-
- /* No error means that we found some unused parameters. */
-
- if (err == noErr) {
- event.message = *(long *) &ignoredActualType;
- event.where = *(Point *) &missedKeyword;
- err = errAEEventNotHandled;
- }
-
- /* errAEDescNotFound means that there are no more parameters. If we get
- ** an error code other than that, flag it.
- */
-
- return(err != errAEDescNotFound);
- }
-
-
-
- /*****************************************************************************/
- /*****************************************************************************/
-
-
-
- #pragma segment AppleEvents
- pascal OSErr DoAEOpenApplication(AppleEvent *message, AppleEvent *reply, long refcon)
- {
- #pragma unused (message, refcon)
-
- FileRecHndl frHndl;
- OSErr err;
-
- gCurrentCursor = nil;
- /* Force re-calc of cursor region and cursor to use. */
-
- err = AppNewDocument(&frHndl);
- if (!err)
- if (err = AppNewWindow(frHndl, nil))
- AppDisposeDocument(frHndl);
-
- AEPutParamPtr( /* RETURN REPLY ERROR, EVEN IF NONE... */
- reply, /* The AppleEvent. */
- keyReplyErr, /* AEKeyword */
- typeShortInteger, /* Desired type. */
- (Ptr)&err, /* Pointer to area for data. */
- sizeof(short) /* Size of data area. */
- );
-
- return(err);
- }
-
-
-
- /*****************************************************************************/
-
-
-
- #pragma segment AppleEvents
- pascal OSErr DoAEOpenDocuments(AppleEvent *message, AppleEvent *reply, long refcon)
- {
- #pragma unused (refcon)
-
- OSErr err;
-
- gCurrentCursor = nil;
- /* Force re-calc of cursor region and cursor to use. */
-
- err = OpenDocEventHandler(message, reply, 0);
-
- AEPutParamPtr( /* RETURN REPLY ERROR, EVEN IF NONE... */
- reply, /* The AppleEvent. */
- keyReplyErr, /* AEKeyword */
- typeShortInteger, /* Desired type. */
- (Ptr)&err, /* Pointer to area for data. */
- sizeof(short) /* Size of data area. */
- );
-
- return(err);
- }
-
-
-
- /*****************************************************************************/
-
-
-
- #pragma segment AppleEvents
- pascal OSErr DoAEPrintDocuments(AppleEvent *message, AppleEvent *reply, long refcon)
- {
- #pragma unused (refcon)
-
- OSErr err;
- short openMode;
-
- gCurrentCursor = nil;
- /* Force re-calc of cursor region and cursor to use. */
-
- openMode = 1;
- if (!AEInteractWithUser(kTimeOutInTicks, nil, nil))
- ++openMode;
-
- err = OpenDocEventHandler(message, reply, openMode);
-
- AEPutParamPtr( /* RETURN REPLY ERROR, EVEN IF NONE... */
- reply, /* The AppleEvent. */
- keyReplyErr, /* AEKeyword */
- typeShortInteger, /* Desired type. */
- (Ptr)&err, /* Pointer to area for data. */
- sizeof(short) /* Size of data area. */
- );
-
- return(err);
- }
-
-
-
- /*****************************************************************************/
-
-
-
- #pragma segment AppleEvents
- pascal OSErr DoAEQuitApplication(AppleEvent *message, AppleEvent *reply, long refcon)
- {
- #pragma unused (message, refcon)
-
- OSErr err;
-
- gCurrentCursor = nil;
- /* Force re-calc of cursor region and cursor to use. */
-
- gQuitApplication = true;
-
- err = noErr;
- AEPutParamPtr( /* RETURN REPLY ERROR, EVEN IF NONE... */
- reply, /* The AppleEvent. */
- keyReplyErr, /* AEKeyword */
- typeShortInteger, /* Desired type. */
- (Ptr)&err, /* Pointer to area for data. */
- sizeof(short) /* Size of data area. */
- );
-
- return(noErr);
- }
-
-
-
- /*****************************************************************************/
-
-
-
- /* OpenDocEventHandler
- **
- ** Called when we recieve an AppleEvent with an ID of "kAEOpenDocuments".
- ** This routine gets the direct parameter, parses it up into little FSSpecs,
- ** and opens each indicated file. It also shows the technique to be used in
- ** determining if you are doing everything the AppleEvent record is telling
- ** you. Parameters can be divided up into two groups: required and optional.
- ** Before executing an event, you must make sure that you've read all the
- ** required events. This is done by making an "any more?" call to the
- ** AppleEvent manager.
- */
-
- #pragma segment AppleEvents
- OSErr OpenDocEventHandler(AppleEvent *message, AppleEvent *reply, short mode)
- {
- #pragma unused (reply)
-
- OSErr err;
- OSErr err2;
- AEDesc theDesc;
- FSSpec theFSS;
- short loop;
- long numFilesToOpen;
- AEKeyword ignoredKeyWord;
- DescType ignoredType;
- Size ignoredSize;
- FileRecHndl frHndl;
- WindowPtr docWindow;
-
- theDesc.dataHandle = nil;
- /* Make sure disposing of the descriptors is okay in all cases.
- ** This will not be necessary after 7.0b3, since the calls that
- ** attempt to create the descriptors will nil automatically
- ** upon failure. */
-
- if (err = AEGetParamDesc(message, keyDirectObject, typeAEList, &theDesc))
- return(err);
-
- if (!MissedAnyParameters(message)) {
-
- /* Got all the parameters we need. Now, go through the direct object,
- ** see what type it is, and parse it up. */
-
- err = AECountItems(&theDesc, &numFilesToOpen);
- if (!err) {
- /* We have numFilesToOpen that need opening, as either a window
- ** or to be printed. Go to it... */
-
- for (loop = 1; ((loop <= numFilesToOpen) && (!err)); ++loop) {
- err = AEGetNthPtr( /* GET NEXT IN THE LIST... */
- &theDesc, /* List of file names. */
- loop, /* Item # in the list. */
- typeFSS, /* Item is of type FSSpec. */
- &ignoredKeyWord, /* Returned keyword -- we know. */
- &ignoredType, /* Returned type -- we know. */
- (Ptr)&theFSS, /* Where to put the FSSpec info. */
- sizeof(theFSS), /* Size of the FSSpec info. */
- &ignoredSize /* Actual size -- we know. */
- );
- if (err) break;
-
- err = AppOpenDocument(&frHndl, &theFSS, fsRdWrPerm);
- if (err) break;
-
- gPrintPage = mode;
- /* Open the window off-screen if we are printing. */
- if (err = AppNewWindow(frHndl, &docWindow))
- AppDisposeDocument(frHndl);
- else {
- if (gPrintPage) {
- err = AppPrintDocument(frHndl, (mode == 2), (loop == 1));
- mode = 1;
- AppDisposeDocument(frHndl);
- DisposeAnyWindow(docWindow);
- }
- }
- gPrintPage = 0;
- }
- }
- }
- AppPrintDocument(nil, false, false); /* Clean up after printing, if we did any. */
-
- err2 = AEDisposeDesc(&theDesc);
- return(err ? err : err2);
- }
-
-
-
-